#!/usr/bin/env node

/*====================================================*/
/* Copyright 2011 Justin Middleton                    */
/* License: Eclipse Public License v1.0,              */
/*          http://www.eclipse.org/legal/epl-v10.html */
/*====================================================*/


RadiusConstants = require("./RadiusConstants.js")
ArrayUtils      = require("./ArrayUtils.js")
Bytes           = require("./Bytes.js")
/*
RADIUS packet format:
Byte     0: CODE
Byte     1: IDENTIFIER
Bytes  2-3: Total packet length (incl code, ID, authenticator, attrs)
Bytes 4-20: Authenticator (16-byte MD5 hash)
Bytes  21-: Attrs
*/

function RadiusPacket(code, attrs, identifier) {
    this.code = (code == undefined) ? 0 : code
    this.attrs = attrs ? attrs : {}
    this.identifier = (identifier == undefined) ? 0 : identifier
    
    this.toString = function() {
        return "Code:       " + code + "\n" +
               "Identifier: " + identifier + "\n" +
               "Data:       " + JSON.stringify(data) + "\n"
    }
    
    this.checkAttrName = function(name) {
        if (! code in RadiusConstants.radiusAttrToAttrCode){
            throw new Error("Unrecognized attribute name [" + name + "].")
        }
    }
    
    this.getAttr = function (name) {
        this.checkAttrName(name)
        code = RadiusConstants.radiusAttrToAttrCode[name]
        return this.attrs[code]
    }
    
    /* ====================================================================
     * Sets an attribute by name.  If the name is not supported, will
     * raise an exception.  If the value is > MAX_ATTR_SIZE, will raise
     * an exception.
     */
    
    this.setAttr = function(name, value) {
        this.checkAttrName(name)
        code = RadiusConstants.radiusAttrToAttrCode[name]
        this.attrs[code] = value
        return this
    }
    
    this.makeAuthenticator = function() {
        
    }
    
    this.packedCode = function() {
        return Bytes.packChar(this.code)
    }
    
    this.packedIdentifier = function () {
        return Bytes.packChar(this.identifier)
    }
    
    this.packedAttrs = function() {
        return packAttrs(this.attrs)
    }
    
    // returns a buffer with the marshalled packet.
    this.marshall = function () {
        packedAttrs = this.packedAttrs()
        length = packedAttrs.length + 2 // add 2 for code and identifier
        console.log("Length: " + length)
        byteArray = []
        byteArray = byteArray.concat(Bytes.packChar(this.code), Bytes.packChar(this.identifier), Bytes.packShort(length), packedAttrs)
        return byteArray
    }
}

// rakes a datagram and returns an unmarshalled RadiusPacket.
function unmarshall (buffer) { }

/* ============================================================
 * Given a dict in {code: value} form, return a byte array in key-length-value encoding.
 */
 
function packAttrs(attrs) {
    byteArray = []
    for (var code in attrs) {
        byteArray = byteArray.concat(packAttr(code, attrs[code]))
    }
    return byteArray
}


/* ============================================================
 * Takes a code (0-255) and a value, and packs the whole mess into
 * a RADIUS packet attribute.  The returned byte array is organized thusly:
 * BYTE 0: Code
 * BYTE 1: Length
 * BYTES 2...: The UTF-8 encoded value; max length 255 bytes.
 */
 
function packAttr(code, value) {
    byteArray = []
    type = RadiusConstants.radiusAttrCodeToType[code]
    if (!type) {
        throw new Error ("Unrecognized attribute code [" + code + "].")
    }
    switch (type) {
        case 'string': {
            valBytes = Bytes.packString(value)
            break
        }
        case 'char': {
            valBytes = Bytes.packChar(value)
            break
        }
        case 'short': {
            valBytes = Bytes.packShort(value)
            break
        }
        case 'int': {
            valBytes = Bytes.packNumber(4, value)
            break
        }
        case 'address': {
            valBytes = Bytes.packIPV4Address(value)
            break
        }
    }
    length = valBytes.length <= 255 ? valBytes.length : 255
    return byteArray.concat(Bytes.packChar(code), Bytes.packChar(length), valBytes.slice(0, length+1))
}



function makeAuthenticator(myPacket, requestPacket, secret) {
    hashFodder = []
    myPackedAttrs = myPacket.packedAttrs()
    
    if (! requestPacket) {
        authenticator = RadiusConstants.sixteenZeroes
    } else {
        authenticator = inRequestPacket.authenticator(secret)
    }

    hashFodder = hashFodder.concat(
        myPacket.packedCode(),
        myPacket.packedIdentifier(),
        packedShort(myPackedAttrs.length),
        authenticator,
        myPackedAttrs,
        secret
    )
    
    md5 = crypto.createHash('md5')
    md5.update(new Buffer(hashFodder))
    return Bytes.packString(md5.digest('binary'))
}

function hello (to) {
    console.log ("Hello, " + to)
    packet = new RadiusPacket (23, {name: "value"}, 199)
    packet.setAttr('callingstationid', 'flimflam')
    console.log(packet.marshall())
}

// hello("justin")

